import java.awt.image.BufferedImage;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FilenameFilter;
import java.io.IOException;
import java.io.PrintWriter;
import java.util.HashMap;
import java.util.Scanner;

import javax.imageio.ImageIO;

import imageCleanup.CleanupFilter;

import preproc.Image;
import preproc.ImageReader;
import segmentation.Segmenter;
import textPixelId.TextId;
import utils.Constants;
import utils.Utils;
import weka.classifiers.functions.LibSVM;
import weka.core.converters.LibSVMLoader;
import wlsvm.WLSVM;


import charRecognition.Normalizer;
import charRecognition.Recognizer;
import charRecognition.WekaUtils;


public class Main {
	Image image;
	
	Normalizer normalizer = new Normalizer();
	Recognizer recognizer = new Recognizer();
	ImageReader reader = new ImageReader();
	CleanupFilter cleanup = new CleanupFilter();
	TextId textId = new TextId();
	Segmenter segmenter = new Segmenter();
	
	
	private void init() {
		WekaUtils.initClasses();
	}
	
	public void trainSVM()
	{
		if (new File(Constants.SVM_MODEL_DIR+"lower_a.model").exists()) {
			// model already trained
			System.out.println("Loading the model from the file ("+Constants.MODEL_FILE+")");
			recognizer.loadSVMModel();	// load the model from the file
			System.out.println("Model loaded");
			return;	
		}
		else
		{
			trainSVM("lower");
			trainSVM("upper");
			trainSVM("nums");
		}
	}
	public void trainSVM(String char_case)
	{	
		
		
		Segmenter.num_chars = Constants.NUM_CHARS_TRAIN;
		File dir = new File(Constants.SVMPATH+char_case);

		String[] children = dir.list(Utils.filter);
		if (children == null) {
		    // Either dir does not exist or is not a directory
		} else {
		    for (int i=0; i<children.length; i++) {
		         String filename = children[i];
		         File innerDir = new File(Constants.SVMPATH+char_case+"\\"+filename);
		         String [] images = innerDir.list(Utils.filter);
		         	System.out.println("lower_"+filename+".arff");
		 	PrintWriter pw = null;
		 		try {
		 			double[] features;
		 			pw = new PrintWriter(new File("SVMTrain\\"+char_case+"_"+filename+".arff"));
		 			WekaUtils.printArffHeader(pw,filename);
		 			for (int j=0;j<images.length;j++) 
			 		{
			 			processImage(Constants.SVMPATH+char_case+"\\"+filename+"\\" + images[j]);
						features = image.chars.get(0).extractFeatures();
						WekaUtils.writeInstanceToFile(pw, features, filename);				
			//			break;
					}
		 			String [] cases={"lower","upper","nums"};
		 			for(int p=0;p<cases.length;p++)
		 			{
			 			File insidedir = new File(Constants.SVMPATH+cases[p]);
						String[] insidechildren = insidedir.list(Utils.filter);
						for (int l=0; l<insidechildren.length; l++) {
							if(!insidechildren[l].equals(filename)){
							  File veryinnerDir = new File(Constants.SVMPATH+cases[p]+"\\"+insidechildren[l]);
						         String [] veryinnerDirimages = veryinnerDir.list(Utils.filter);
						         for(int m=0;m<veryinnerDirimages.length;m++){
						        	 processImage(Constants.SVMPATH+cases[p]+"\\"+insidechildren[l]+"\\" + veryinnerDirimages[m]);
									features = image.chars.get(0).extractFeatures();
							WekaUtils.writeInstanceToFile(pw, features, "other");
							break;
							}
							}
						}
					}
		 		} catch (FileNotFoundException e) {
		 			e.printStackTrace();
		 			//System.exit(0);
		 		}	 		
		 		
		 		pw.close();
				Segmenter.num_chars = Constants.NUM_CHARS_EVAL;
				recognizer.trainSVMClassifier(filename,char_case+"_"+filename);
		 		}

		    }
		}

		
	
	public void train() {
		
		if (new File(Constants.MODEL_FILE).exists()) {
			// model already trained
			System.out.println("Loading the model from the file ("+Constants.MODEL_FILE+")");
			recognizer.loadModel();	// load the model from the file
			System.out.println("Model loaded");
			return;	
		}

		if (!new File(Constants.TRAIN_ARFF).exists()) {			
			
			Segmenter.num_chars = Constants.NUM_CHARS_TRAIN;
			
			// Write an ARFF file that contains the feature values for each image
			Scanner s = null;
			try {
				s = new Scanner(new File(Constants.TRAIN_DIR + Constants.CORRECT));
			} catch (FileNotFoundException e) {
				e.printStackTrace();
				System.exit(0);
			} 
			PrintWriter pw = null;
			try {
				pw = new PrintWriter(new File(Constants.TRAIN_ARFF));
			} catch (FileNotFoundException e) {
				e.printStackTrace();
				System.exit(0);
			}
			WekaUtils.printArffHeader(pw);
			while (s.hasNext()) {
				// read a new line (pair of strings) from the 'correct.txt' file
				String f = s.next();
				String c = s.next();
				System.out.println(f + " " + c);
				processImage(Constants.TRAIN_DIR + Constants.IMAGES_DIR + f);
				double[] features = image.chars.get(0).extractFeatures();
				WekaUtils.writeInstanceToFile(pw, features, c);
	//			break;
			}
			pw.close();
			Segmenter.num_chars = Constants.NUM_CHARS_EVAL;
		}
		
		// Train the classifier
		System.out.println("Training the classifier (may take a while).");
		recognizer.trainClassifier();
		System.out.println("Done.");
		recognizer.saveModel();
	

	}
	
	public void evaluate() {
		File fileResults = new File(Constants.TEST_DIR + Constants.CORRECT);
		Scanner s = null;
		try {
			s = new Scanner(fileResults);
		} catch (FileNotFoundException e) {
			e.printStackTrace();
			System.exit(0);
		}
		
		Statistics statistics = new Statistics();
		int nr = 0;
		while (s.hasNext()) {
			nr++;
			String fileName = s.next();
			String correct = s.next();
			
			if (nr<0) continue;
			String filePath = Constants.TEST_DIR + Constants.IMAGES_DIR + fileName;
			processImage(filePath);
			String txt = solveImage();

			
//			System.out.println(filePath);
			System.out.println(nr + ". Predicted: " + txt + " Correct: " + correct);
			statistics.update(correct, txt);
			
//			if (nr > 25) break;
		}
		
		
		File dirImgs = new File(Constants.TEST_DIR + Constants.IMAGES_DIR);
		statistics.printStatistics(System.out);
		
	}
	
	// apply some filters on the image (erosion, dilation, thresholding, lone pixel removal)
	private void doCleanup() {
		image.showGreyscale("initial");
		cleanup.cleanImage(image);
		image.showGreyscale("after cleanup");
	}
	
	// identify pixels that belong to text
	// after calling this method we should only have black and white pixels in image
	// (black pixels belong to the text)  
	private void doTextPixelId() {
		textId.identifyText(image);
	}
	
	private void doSegmentation() {
		segmenter.segmentation(image);
	}
	
	private void doNormalization() {
		normalizer.normalize(image);
	}
	
	
	private String doRecognition() {
		//return recognizer.recognizeSVM(image);
		return recognizer.recognize(image);
	}
	
	public void processImage(String imageFileName) {
		image = reader.readImage(imageFileName);
		if (image==null) System.exit(0);
		doCleanup();
		doTextPixelId();
		doSegmentation();
		doNormalization();
	}
	
	public String solveImage() {
		return doRecognition();
	}
	
	
	public void run(String[] args) {
		init();
		train();	// train the classifier
		
		if (args!=null && args.length>0) {
			// if a file name is given as a parameter, then print only the result for that image
			processImage(args[0]);
			String txt = solveImage();
			System.out.println("Text: " + txt);
		}
		else {
			// evaluate the program on the directory with test images
			evaluate();
		}
	}
	
	public static void main(String[] args) {
		Constants.DEBUG = true;
		
		// image8, image11, image14, image16, image28
		if (Constants.DEBUG) args = new String[] {"testData\\images\\image8.jpg"};
			
		Main main = new Main();
		main.run(args);
		//pentru rulat SVM se foloseste codul urmator si se modifica functia doRecognition
		/*main.init();
		main.trainSVM();
		if (args!=null && args.length>0) {
			// if a file name is given as a parameter, then print only the result for that image
			main.processImage(args[0]);
			String txt = main.solveImage();
			System.out.println("Text: " + txt);
		}
		else {
			// evaluate the program on the directory with test images
			main.evaluate();
		}
	*/
		}
}
